/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support  -  ROUSSET  -
 * ----------------------------------------------------------------------------
 * Copyright (c) 2006, Atmel Corporation

 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer below in the documentation and/or
 * other materials provided with the distribution.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------
#include "../common/applet.h"
#include <board.h>
#include <board_memories.h>
#include <pio/pio.h>
#include <memories/norflash/NorFlashCFI.h>
#include <memories/norflash/NorFlashApi.h>
#include <utility/math.h>
#include <utility/trace.h>


//------------------------------------------------------------------------------
//         Local definitions
//------------------------------------------------------------------------------
/// Software version.
#define VERSION "1.1"

/// Max size of read and write buffer.
#define MAX_BUFFER_SIZE  0x10000


//------------------------------------------------------------------------------
//         Local structures
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Structure for storing parameters for each command that can be performed by
/// the applet.
//------------------------------------------------------------------------------
struct _Mailbox {

    /// Command send to the monitor to be executed.
    unsigned int command;
    /// Returned status, updated at the end of the monitor execution.
    unsigned int status;

    /// Input Arguments in the argument area
    union {

        /// Input arguments for the Init command.
        struct {

            /// Communication link used.
            unsigned int comType;
            /// Trace level.
            unsigned int traceLevel;

        } inputInit;

        /// Output arguments for the Init command.
        struct {
            
            /// Memory size.
            unsigned int memorySize;
            /// Buffer address.
            unsigned int bufferAddress;
            /// Buffer size.
            unsigned int bufferSize;
        } outputInit;

        /// Input arguments for the Write command.
        struct {

            /// Buffer address.
            unsigned int bufferAddr;
            /// Buffer size.
            unsigned int bufferSize;
            /// Memory offset.
            unsigned int memoryOffset;

        } inputWrite;

        /// Output arguments for the Write command.
        struct {

            /// Bytes written.
            unsigned int bytesWritten; 
        } outputWrite;

        /// Input arguments for the Read command.
        struct {

            /// Buffer address.
            unsigned int bufferAddr;
            /// Buffer size.
            unsigned int bufferSize;
            /// Memory offset.
            unsigned int memoryOffset;

        } inputRead;

        /// Output arguments for the Read command.
        struct {

            /// Bytes read.
            unsigned int bytesRead;

        } outputRead;

        /// Input arguments for the Full Erase command.
        // NONE
        
        /// Output arguments for the Full Erase command.
        // NONE
        
        /// Input arguments for the Buffer Erase command.
        struct {

            /// Memory offset to be erase.
            unsigned int memoryOffset;

        } inputBufferErase;
        
        /// Output arguments for the Buffer Erase command.
        struct {

            /// erased size in bytes
            unsigned int bytesErased;

        } outputBufferErase;
        
    } argument; 
};

//------------------------------------------------------------------------------
//         Global variables
//------------------------------------------------------------------------------

/// End of program space (code + data).
extern unsigned int end;

/// Trace level can be set at applet initialization
#if defined(DYNAMIC_TRACE_LEVEL)
    unsigned int trace_LEVEL = 0;
#endif

//------------------------------------------------------------------------------
//         Local variables
//------------------------------------------------------------------------------

/// Norflash device structure.
static struct NorFlash norFlash;

/// Last erased sector id, this will avoid to erase if the sector is already erased.
static unsigned short lastErasedSector = 0xFFFF;

/// Indicate the farthest memory offset ever erase, if current write address is less 
/// than the lastErasedOffset, the related sector should be erase again before write.
static unsigned int lastErasedOffset = 0;

/// NOR Flash memory size (in bytes) 
unsigned int memSize = 0;

#ifdef PINS_NORFLASH
/// Pins used to access to norflash.
static const Pin pPinsNf[] = {PINS_NORFLASH};
#else
/// Pins used to access to nandflash.
static const Pin pPinsNf[] = {{0, 0, 0, 0, 0}};
#endif

//------------------------------------------------------------------------------
//         Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Applet main entry. This function decodes received command and executes it.
/// \param argc  always 1
/// \param argv  Address of the argument area.
//------------------------------------------------------------------------------
int main(int argc, char **argv)
{
    struct _Mailbox *pMailbox = (struct _Mailbox *) argv;
    unsigned int bufferSize, bufferAddr, memoryOffset;
    unsigned int  eraseStartSector, eraseEndSector, bytesToErase;
    unsigned int i;
    const unsigned char busWidth[3] = {FLASH_CHIP_WIDTH_8BITS, FLASH_CHIP_WIDTH_16BITS, FLASH_CHIP_WIDTH_32BITS};
    
    trace_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
    PIO_Configure(pPinsNf, PIO_LISTSIZE(pPinsNf));

    // ----------------------------------------------------------
    // INIT: 
    // ----------------------------------------------------------
    if (pMailbox->command == APPLET_CMD_INIT) {
        
#if defined(DYNAMIC_TRACE_LEVEL)
        trace_LEVEL = pMailbox->argument.inputInit.traceLevel;
#endif

        trace_LOG(trace_INFO, "-- NorFlash ISP Applet %s %s %s --\n\r", VERSION, __DATE__, __TIME__);
        trace_LOG(trace_INFO, "-I- INIT command\n\r");

        norFlash.norFlashInfo.baseAddress = BOARD_NORFLASH_ADDR;
        // Check device CFI and get Vendor setting from it.
        trace_LOG(trace_INFO, "-I- \t Common Flash Interface detecting...\n\r");
        
        for (i = 0; i < 3; i++) {
            // Configure SMC for Norflash accesses 
            trace_LOG(trace_INFO, "-I- \t Try bus width %d bits\n\r", busWidth[i] * 8);
            BOARD_ConfigureNorFlash(busWidth[i] * 8);
            if (!NorFlash_CFI_Detect(&norFlash, busWidth[i])) break;
        }

        if (norFlash.norFlashInfo.cfiCompatible == 0) {
            pMailbox->status = APPLET_DEV_UNKNOWN;
            pMailbox->argument.outputInit.bufferSize = 0;
            pMailbox->argument.outputInit.memorySize = 0;
            trace_LOG(trace_INFO, "-E- \tDevice Unknown\n\r");
            goto exit;
        }
        else {
            trace_LOG(trace_INFO, "-I- \t manufactureID : 0x%08x, deviceID : 0x%08x\n\r",
                      NORFLASH_ReadManufactoryID(&norFlash),
                      NORFLASH_ReadDeviceID(&norFlash));
            NORFLASH_ReadManufactoryID(&norFlash);
            // Get device parameters
            memSize = NorFlash_GetDeviceSizeInBytes(&(norFlash.norFlashInfo));
            pMailbox->argument.outputInit.bufferAddress = ((unsigned int) &end);
            bufferSize = NorFlash_GetDeviceMaxBlockSize(&(norFlash.norFlashInfo));
            if (bufferSize > MAX_BUFFER_SIZE) bufferSize = MAX_BUFFER_SIZE;
            pMailbox->argument.outputInit.bufferSize = bufferSize;
            pMailbox->argument.outputInit.memorySize = memSize;

            trace_LOG(trace_INFO, "-I- \t bufferSize : %d memorySize : %d bufferAddr: 0x%x \n\r",
                      pMailbox->argument.outputInit.bufferSize,
                      pMailbox->argument.outputInit.memorySize,
                      (unsigned int) &end);
        }
    }

    // ----------------------------------------------------------
    // WRITE: 
    // ----------------------------------------------------------
    else if (pMailbox->command == APPLET_CMD_WRITE) {
        memoryOffset = pMailbox->argument.inputWrite.memoryOffset;
        bufferAddr = pMailbox->argument.inputWrite.bufferAddr;
        bufferSize = pMailbox->argument.inputWrite.bufferSize;

        trace_LOG(trace_INFO, 
                  "-I- WRITE at offset: 0x%x buffer at : 0x%x of: 0x%x Bytes\n\r",
                  memoryOffset, bufferAddr, bufferSize);

        if (memoryOffset < lastErasedOffset) {
            lastErasedSector = 0xFFFF;
        }
        lastErasedOffset = memoryOffset + bufferSize;
        eraseStartSector = NorFlash_GetDeviceSectorInRegion(&(norFlash.norFlashInfo), memoryOffset);
        eraseEndSector = NorFlash_GetDeviceSectorInRegion(&(norFlash.norFlashInfo), lastErasedOffset);
        do {
        	trace_LOG(trace_INFO, "-I- lastErasedSector %d \n\r", lastErasedSector);
        	if (eraseStartSector > lastErasedSector || lastErasedSector == 0xFFFF) {
        		trace_LOG(trace_INFO, "-I- Sector Erase in sector %d \n\r", eraseStartSector);
                if (NORFLASH_EraseSector(
                	&norFlash, 
                    NorFlash_GetDeviceSectorAddress(&(norFlash.norFlashInfo),eraseStartSector))) {
                        trace_LOG(trace_INFO, "-I- Sector Erase failed in sector %d \n\r", eraseStartSector);
                    pMailbox->status = APPLET_FAIL;
                    goto exit;
                }
                lastErasedSector = eraseStartSector;
        	}
        	eraseStartSector++;
        } while (eraseStartSector <= eraseEndSector);
        // Write the buffer contents.
        if (NORFLASH_WriteData(&norFlash, 
                               memoryOffset, 
                               (unsigned char *)bufferAddr,
                               bufferSize)) {
            trace_LOG(trace_INFO, "-I- Program failed \n\r");
            pMailbox->status = APPLET_FAIL;
            goto exit;
        }
        pMailbox->argument.outputWrite.bytesWritten = bufferSize;
        pMailbox->status = APPLET_SUCCESS;
    }

    // ----------------------------------------------------------
    // READ: 
    // ----------------------------------------------------------
    else if (pMailbox->command == APPLET_CMD_READ) {
        memoryOffset = pMailbox->argument.inputRead.memoryOffset;
        bufferAddr   = pMailbox->argument.inputRead.bufferAddr;
        bufferSize   = pMailbox->argument.inputRead.bufferSize;

        trace_LOG(trace_INFO, 
                  "-I- READ at offset: 0x%x buffer at : 0x%x of: 0x%x Bytes\n\r",
                  memoryOffset, bufferAddr, bufferSize);
        NORFLASH_ReadData(&norFlash, memoryOffset, (unsigned char *) bufferAddr, bufferSize);
        pMailbox->argument.outputRead.bytesRead = bufferSize;
        pMailbox->status = APPLET_SUCCESS;
    }
	// ----------------------------------------------------------
    // BUFFER ERASE: 
    // ----------------------------------------------------------
    else if (pMailbox->command == APPLET_CMD_BUFFER_ERASE) {
        trace_LOG(trace_INFO, "-I- BUFFER ERASE command\n\r");
        memoryOffset = pMailbox->argument.inputBufferErase.memoryOffset;
        
        bytesToErase = NorFlash_GetDeviceMaxBlockSize(&(norFlash.norFlashInfo)) * 8;
        bytesToErase = ((memoryOffset + bytesToErase) > memSize) ? (memSize - memoryOffset): bytesToErase;
        lastErasedOffset = memoryOffset + bytesToErase;
        eraseStartSector = NorFlash_GetDeviceSectorInRegion(&(norFlash.norFlashInfo), memoryOffset);
        eraseEndSector = NorFlash_GetDeviceSectorInRegion(&(norFlash.norFlashInfo), lastErasedOffset);
        for (i = eraseStartSector; i <= eraseEndSector; i++) {
            if (NORFLASH_EraseSector(
            	&norFlash, 
                NorFlash_GetDeviceSectorAddress(&(norFlash.norFlashInfo),i))) {
                    trace_LOG(trace_INFO, "-I- Sector Erase failed in sector %d \n\r", i);
                pMailbox->status = APPLET_FAIL;
                goto exit;
            }
            lastErasedSector = i;
        }
        pMailbox->argument.outputBufferErase.bytesErased = bytesToErase;
        trace_LOG(trace_INFO, "-I- Buffer Erase achieved\n\r");
        pMailbox->status = APPLET_SUCCESS;
    }
    
    // ----------------------------------------------------------
    // FULL ERASE: 
    // ----------------------------------------------------------
    else if (pMailbox->command == APPLET_CMD_FULL_ERASE) {
        trace_LOG(trace_INFO, "-I- FULL ERASE command\n\r");
        if (NORFLASH_EraseChip(&norFlash)) {
            trace_LOG(trace_INFO, "-I- Full Erase failed! \n\r");
            pMailbox->status = APPLET_FAIL;
            goto exit;
        }
        trace_LOG(trace_INFO, "-I- Full Erase achieved\n\r");
        pMailbox->status = APPLET_SUCCESS;
    }
    

exit :
    // Acknowledge the end of command
    trace_LOG(trace_INFO, "-I- \tEnd of Applet %x %x.\n\r", pMailbox->command, pMailbox->status);
    // Notify the host application of the end of the command processing
    pMailbox->command = ~(pMailbox->command);

    return 0;
}

